  <html>
<head>
<title>Remake de Fruity Frank - Version 0.24</title>
<style type="text/css">
span {
	font-family: 'Courier New';
	font-size: 10pt;
	color: #000000;
}
.SpanClass0 {
	color: #808080;
}
.SpanClass3 {
	color: #008080;
}
.SpanClass5 {
	font-weight: bold;
	color: #0000FF;
}
.SpanClass7 {
	color: #808080;
}
.SpanClass10 {
	font-weight: bold;
	color: #000080;
}
.SpanClass11 {
}
</style>
</head>
<body bgcolor="#FFFFFF">
<h2>Remake (parcial) de Fruity Frank... 24 - Enemigos m&aacute;s inteligentes.</h2>
<p align="right">(Volver al <a href="doc.html">&iacute;ndice</a>) </p>

<p>Hasta ahora nuestros enemigos se mov&iacute;an simplemente de lado a lado. Ahora vamos a intentar que el movimiento sea un poco m&aacute;s &quot;inteligente&quot;, y m&aacute;s parecido al del Fruity Frank original.</p>

<p>Antes ten&iacute;amos un &uacute;nico m&eacute;todo &quot;Mover&quot;, com&uacute;n a todos los elementos de la clase &quot;Enemigo&quot;. Ahora debemos hacer que no todos los enemigos se comporten igual:</p>

<ul>
  <li>En primer lugar, no todos aparecer&aacute;n en el instante inicial: cada uno tendr&aacute; un retardo algo mayor que los anteriores, para que aparezcan uno por uno.</li>
  <li>Para seguir, la inteligencia de los movimientos que har&aacute; cada uno depender&aacute; del tipo de enemigo del que se trate. Para m&aacute;s detalles:</li>
    <ul>
    <li>Los &quot;se&ntilde;ores nariz&quot; se mover&aacute;n casi siempre por los huecos de la pantalla, y pocas veces romper&aacute;n paredes.</li>
    <li>Los &quot;se&ntilde;ores pepino&quot; ser&aacute;n mucho m&aacute;s impacientes, y romper&aacute;n paredes con m&aacute;s facilidad.</li>
    </ul>
  <li>Ambos tipos de enemigos se mueven b&aacute;sicamente al azar, tendiendo a proseguir su marcha hasta que choquen con algo, momento en el que cambiar&aacute;n de sentido. M&aacute;s adelante habr&aacute; un tercer enemigo, la &quot;fresa&quot;, cuyo movimiento ser&aacute; b&aacute;sicamente para perseguir al personaje.</li>
  <li>Adem&aacute;s, cada tipo enemigo aparece en un punto distinto de la pantalla:</li>
    <ul>
    <li>Los &quot;nariz&quot; salen desde una zona central de la pantalla, que llamaremos su &quot;nido&quot;.</li>
    <li>Los &quot;pepino&quot; caen desde la parte superior.</li>
    </ul>
</ul>

<p>Vayamos viendo c&oacute;mo hacer todo esto...</p>

<p>Para que los enemigos puedan saber si se pueden mover a una cierta casilla har&aacute; falta que se puedan comunicar con el nivel que los contiene, as&iacute; que en el constructor les indicaremos cual es ese nivel:</p>

<pre><code>    public Nariz(Nivel n) {
      miNivel = n;
    ...</code></pre>  

<p>Adem&aacute;s, la funci&oacute;n &quot;EsPosibleMover&quot; que ya exist&iacute;a estaba pensada para el personaje, que pod&iacute;a atravesar paredes y comer frutas, pero los enemigos no pueden comer frutas, y se mueven preferentemente por espacios huecos. Por eso, nos interesar&aacute; una nueva funci&oacute;n, que podr&iacute;a llamarse &quot;MovilidadEnemigo&quot;, y que tendr&iacute;a 3 valores posibles: </p>

<ul>
  <li>Valdr&iacute;a 0 para indicar cuando no puede entrar a esa posici&oacute;n, porque haya una fruta, una manzana u otro enemigo.</li>
  <li>Ser&iacute;a 1 cuando puede entrar con dificultad, rompiendo paredes, algo que los Nariz pocas veces har&aacute;n, pero los Pepino s&iacute; podr&aacute;n hacer con m&aacute;s frecuencia.</li>
  <li>Ser&aacute; 2 cuando sea espacio hueco, por el que se pueden desplazar sin problema.</li>
  <li>Tambi&eacute;n habr&iacute;a que comprobar si en esa casilla ya hay otro enemigo (o si ya est&aacute; entrando en ella), porque no se deber&aacute;n solapar dos en la misma posici&oacute;n de pantalla, pero esto lo dejamos para un poco m&aacute;s adelante.</li>
</ul>

<pre><code>    /// Indica si es el enemigo puede moverse a cierta posicion de la pantalla
    /// (2 = s&iacute;, hueco; 1 = a veces, pared; 0 = nunca, fruta).
    public  byte MovilidadEnemigo(short x, short y)
    {
      short xMapa = (short) ((x-xIniPantalla)/anchoCasilla);
      short yMapa = (short) ((y-yIniPantalla)/altoCasilla);

      if  ((x &lt; xIniPantalla) || (xMapa &gt;= MAXCOLS) ||  // Si se sale, no puede
        (y &lt; yIniPantalla) || (yMapa &gt;= MAXFILAS)) return 0;
        
      char simbolo = GetPosicion(xMapa, yMapa);
        
      if  ((simbolo == ' ')     // Si es hueco, s&iacute; puede
          || (simbolo == 'N'))  // Tambi&eacute;n si es el nido
        return 2;  
        
      if  ((simbolo == 'X')  // Si es pared, puede con dificultad
         || (simbolo == 'Y') || (simbolo == 'Z') )
        return 1;
      
      // En el resto de casos, ser&iacute;a una fruta y no puede
      return 0;
    }</code></pre>  
        
<p>As&iacute;, la l&oacute;gica del movimiento de los enemigos &quot;Nariz&quot; podr&iacute;a ser as&iacute;:</p>

<ul>
  <li>Si se estaba moviendo, continuar&aacute; en la misma direcci&oacute;n hasta que tope con un obst&aacute;culo.</li>
  <li>Cuando encuentre un obst&aacute;culo, elegir&aacute; una nueva direccci&oacute;n al azar. Empezar&aacute; a a moverse en esa direcci&oacute;n si no hay obst&aacute;culos; si hay una pared, se empezar&aacute; a mover con una probabilidad baja (por ejemplo, un 25%). Si hay una obst&aacute;culo, o bien si es una pared pero el n&uacute;mero al azar ha indicado que no debe atravesarla, se quedar&aacute; parado, y en el siguiente fotograma de juego se volver&aacute; a elegir una direcci&oacute;n al azar y a repetir el proceso.</li>
</ul>
    
<pre><code>    public override void Mover()
    {
    // Si no est&aacute; activo, espero el tiempo estipulado para que aparezca
    if (!activo)
    {
      contadorHastaRetardo ++;
      if (contadorHastaRetardo &gt;= retardo)
        activo = true;
      return;
    }
    
    // Si est&aacute; parado, busco nueva direcci&oacute;n
    if (parado) {
      calcularNuevaDireccion();
      return;
    }
    
    // Si se puede mover en horizontal o vertical, avanza
    if ((!parado) &amp;&amp; (incrX &gt; 0)) {
      if (miNivel.MovilidadEnemigo( 
          (short) (x+miNivel.GetAnchoCasilla()), y) == 2)
        x  += incrX;
      else
        parado = true;
    }
    ...</code></pre>  

      
<p>Y la rutina de mover el enemigo &quot;Pepino&quot;, que s&iacute; puede atravesar paredes, de momento podr&iacute;a ser muy similar, con la diferencia de que podr&aacute; entrar en casillas de &quot;Movilidad 1&quot; (paredes) y que deber&aacute; borrar la casilla a la que entra:</p>

<pre><code>    ...
    // Si se puede mover en horizontal o vertical, avanza
    if ((!parado) &amp;&amp; (incrX &gt; 0)) {
      if (miNivel.MovilidadEnemigo( 
          (short) (x+miNivel.GetAnchoCasilla()), y) &gt;= 1)
        {
        x  += incrX;
        miNivel.BorrarPosicionPantalla(x,y);
        }
      else
        parado = true;
    }
    ...</code></pre>  

<p>En cuanto a la posici&oacute;n inicial de cada enemigo, la definimos desde el constructor de cada nivel. Por ejemplo, para el Nivel 1, podr&iacute;a ser:</p>

<pre><code>    public  Nivel1()
    {
      byte i;
      
      miMapa = new Mapa1();
      enemigos = new Enemigo[NUMENEMIGOS];

      enemigos[0] = new Nariz(this);
        enemigos[0].MoverA(miMapa.posXnido, miMapa.posYnido); 
        enemigos[0].SetRetardo(25);  // 1 segundo despues del comienzo

      enemigos[1] = new Nariz(this);
        enemigos[1].MoverA(miMapa.posXnido, miMapa.posYnido); 
        enemigos[1].SetRetardo(75);  // 3 segundos despues del comienzo

      enemigos[2] = new Pepino(this);
        enemigos[2].MoverA(
          (short) (miMapa.GetXIni() + 5*miMapa.GetAnchoCasilla()),
          miMapa.GetYIni()); 
        enemigos[2].SetRetardo(150);  // 6 segundos despues del comienzo    
        enemigos[2].SetVelocidad(0,4);
    }</code></pre>  
    
<p>Como siempre, todo el fuente del proyecto est&aacute; en: <a href="http://code.google.com/p/fruityfrank"> code.google.com/p/fruityfrank</a></p>
<p align="right">(Volver al <a href="doc.html">&iacute;ndice</a>) </p>
</body>
</html>
